Here we do exploratory data analysis on HDMA data obtained for Pennsylvania in the year 2016. We will start from looking at the data superficially and then diving into columns of interest. Then we see for any missing values and handle them. Lets get started with the steps. ## Global setup like working directory, data directory etc should happen here. Global setup like working directory, data directory etc should happen here.

library(sys)
working_directory <- getwd()

setwd(dirname(dirname(working_directory)))
The working directory was changed to /Users/omkarpawar/Desktop/csp-571-02-final-project/src inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
writeLines("")
getwd()
[1] "/Users/omkarpawar/Desktop/csp-571-02-final-project/src"
data_dir <- "/Users/omkarpawar/Desktop/Data/PA/"

Install required packages.

# https://stackoverflow.com/questions/4090169/elegant-way-to-check-for-missing-packages-and-install-them
list_of_packages <- c("mlbench", "corrplot", "rvest", "tidyr", "stringr", "dplyr", "lubridate", "data.table", "mice", "scales", "naniar", "rpart", "rpart.plot", "caret")
new.packages <- list_of_packages[!(list_of_packages %in% installed.packages()[,"Package"])]

if (length(new.packages)) {
  print("Installing packages\n")
  install.packages(new.packages())
}

library(corrplot)
library(ggplot2)
library(tidyr)
library(stringr)
library(dplyr)
library(data.table)
library(mice)
library(rstudioapi)    
library(naniar)

source(paste(dirname(dirname(dirname(rstudioapi::getActiveDocumentContext()$path))), "utils/utils.r", sep="/"))
source(paste(dirname(dirname(dirname(rstudioapi::getActiveDocumentContext()$path))), "utils/model_utils.r", sep="/"))

Load data file.

hmda_data_pa <- fread(paste(data_dir, "hmda_2016_pa_all-records_labels.csv", sep = ""))
|--------------------------------------------------|
|==================================================|

Data analysis section 1.

Lets see first few rows of our data and what they tell about the application.

hmda_data_pa_df <- as.data.frame(hmda_data_pa)

colnames(hmda_data_pa_df)
 [1] "as_of_year"                     "respondent_id"                 
 [3] "agency_name"                    "agency_abbr"                   
 [5] "agency_code"                    "loan_type_name"                
 [7] "loan_type"                      "property_type_name"            
 [9] "property_type"                  "loan_purpose_name"             
[11] "loan_purpose"                   "owner_occupancy_name"          
[13] "owner_occupancy"                "loan_amount_000s"              
[15] "preapproval_name"               "preapproval"                   
[17] "action_taken_name"              "action_taken"                  
[19] "msamd_name"                     "msamd"                         
[21] "state_name"                     "state_abbr"                    
[23] "state_code"                     "county_name"                   
[25] "county_code"                    "census_tract_number"           
[27] "applicant_ethnicity_name"       "applicant_ethnicity"           
[29] "co_applicant_ethnicity_name"    "co_applicant_ethnicity"        
[31] "applicant_race_name_1"          "applicant_race_1"              
[33] "applicant_race_name_2"          "applicant_race_2"              
[35] "applicant_race_name_3"          "applicant_race_3"              
[37] "applicant_race_name_4"          "applicant_race_4"              
[39] "applicant_race_name_5"          "applicant_race_5"              
[41] "co_applicant_race_name_1"       "co_applicant_race_1"           
[43] "co_applicant_race_name_2"       "co_applicant_race_2"           
[45] "co_applicant_race_name_3"       "co_applicant_race_3"           
[47] "co_applicant_race_name_4"       "co_applicant_race_4"           
[49] "co_applicant_race_name_5"       "co_applicant_race_5"           
[51] "applicant_sex_name"             "applicant_sex"                 
[53] "co_applicant_sex_name"          "co_applicant_sex"              
[55] "applicant_income_000s"          "purchaser_type_name"           
[57] "purchaser_type"                 "denial_reason_name_1"          
[59] "denial_reason_1"                "denial_reason_name_2"          
[61] "denial_reason_2"                "denial_reason_name_3"          
[63] "denial_reason_3"                "rate_spread"                   
[65] "hoepa_status_name"              "hoepa_status"                  
[67] "lien_status_name"               "lien_status"                   
[69] "edit_status_name"               "edit_status"                   
[71] "sequence_number"                "population"                    
[73] "minority_population"            "hud_median_family_income"      
[75] "tract_to_msamd_income"          "number_of_owner_occupied_units"
[77] "number_of_1_to_4_family_units"  "application_date_indicator"    
writeLines("")
head(hmda_data_pa_df, 10)
NA

Data analysis section 2. Print glimpse of dataset i.e a vertical preview of the dataset.

dim(hmda_data_pa_df)
[1] 526005     78
writeLines("Glimpse of hmda dataset for PA")
Glimpse of hmda dataset for PA
glimpse(hmda_data_pa_df)
Observations: 526,005
Variables: 78
$ as_of_year                     <int> 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, …
$ respondent_id                  <chr> "0000007468", "13-6131491", "0000852320", "26-2261031", "000…
$ agency_name                    <chr> "Federal Deposit Insurance Corporation", "Department of Hous…
$ agency_abbr                    <chr> "FDIC", "HUD", "CFPB", "HUD", "NCUA", "CFPB", "HUD", "HUD", …
$ agency_code                    <int> 3, 7, 9, 7, 5, 9, 7, 7, 9, 7, 7, 7, 3, 7, 7, 7, 7, 7, 7, 9, …
$ loan_type_name                 <chr> "Conventional", "FHA-insured", "Conventional", "Conventional…
$ loan_type                      <int> 1, 2, 1, 1, 1, 4, 3, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, …
$ property_type_name             <chr> "One-to-four family dwelling (other than manufactured housin…
$ property_type                  <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ loan_purpose_name              <chr> "Home purchase", "Home purchase", "Refinancing", "Refinancin…
$ loan_purpose                   <int> 1, 1, 3, 3, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 3, 3, 3, 3, …
$ owner_occupancy_name           <chr> "Owner-occupied as a principal dwelling", "Owner-occupied as…
$ owner_occupancy                <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ loan_amount_000s               <int> 371, 160, 244, 263, 5, 115, 245, 234, 81, 218, 204, 417, 147…
$ preapproval_name               <chr> "Not applicable", "Not applicable", "Not applicable", "Not a…
$ preapproval                    <int> 3, 3, 3, 3, 3, 3, 2, 2, 3, 2, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, …
$ action_taken_name              <chr> "Loan originated", "Loan purchased by the institution", "Loa…
$ action_taken                   <int> 1, 6, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 3, 1, 4, 4, …
$ msamd_name                     <chr> "Pittsburgh - PA", "Pittsburgh - PA", "Montgomery County, Bu…
$ msamd                          <int> 38300, 38300, 33874, 49620, 37964, 23900, 38300, 38300, NA, …
$ state_name                     <chr> "Pennsylvania", "Pennsylvania", "Pennsylvania", "Pennsylvani…
$ state_abbr                     <chr> "PA", "PA", "PA", "PA", "PA", "PA", "PA", "PA", "PA", "PA", …
$ state_code                     <int> 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, …
$ county_name                    <chr> "Butler County", "Allegheny County", "Chester County", "York…
$ county_code                    <int> 19, 3, 29, 133, 101, 1, 3, 3, 107, 133, 101, 91, 133, 3, 125…
$ census_tract_number            <dbl> 9110.00, 4883.00, 3081.02, 224.02, 70.00, 301.02, 5200.01, 4…
$ applicant_ethnicity_name       <chr> "Not Hispanic or Latino", "Not applicable", "Not Hispanic or…
$ applicant_ethnicity            <int> 2, 4, 2, 2, 2, 2, 2, 2, 1, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, …
$ co_applicant_ethnicity_name    <chr> "Not Hispanic or Latino", "Not applicable", "Not Hispanic or…
$ co_applicant_ethnicity         <int> 2, 4, 2, 2, 2, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 5, 2, …
$ applicant_race_name_1          <chr> "White", "Not applicable", "White", "White", "Information no…
$ applicant_race_1               <int> 5, 7, 5, 5, 6, 5, 5, 5, 5, 3, 6, 5, 5, 5, 5, 5, 6, 5, 5, 5, …
$ applicant_race_name_2          <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ applicant_race_2               <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ applicant_race_name_3          <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ applicant_race_3               <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ applicant_race_name_4          <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ applicant_race_4               <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ applicant_race_name_5          <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ applicant_race_5               <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ co_applicant_race_name_1       <chr> "White", "Not applicable", "White", "White", "Information no…
$ co_applicant_race_1            <int> 5, 7, 5, 5, 6, 8, 8, 8, 8, 8, 8, 5, 5, 8, 8, 8, 8, 5, 8, 5, …
$ co_applicant_race_name_2       <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ co_applicant_race_2            <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ co_applicant_race_name_3       <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ co_applicant_race_3            <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ co_applicant_race_name_4       <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ co_applicant_race_4            <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ co_applicant_race_name_5       <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ co_applicant_race_5            <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ applicant_sex_name             <chr> "Male", "Not applicable", "Male", "Male", "Female", "Female"…
$ applicant_sex                  <int> 1, 4, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, …
$ co_applicant_sex_name          <chr> "Female", "Not applicable", "Female", "Female", "Male", "No …
$ co_applicant_sex               <int> 2, 4, 2, 2, 1, 5, 5, 5, 5, 5, 5, 1, 1, 5, 5, 5, 5, 1, 5, 2, …
$ applicant_income_000s          <int> 128, NA, 95, 155, 67, 41, 47, 82, 27, 44, 42, 808, 77, 137, …
$ purchaser_type_name            <chr> "Loan was not originated or was not sold in calendar year co…
$ purchaser_type                 <int> 0, 2, 1, 0, 0, 2, 5, 7, 3, 2, 0, 7, 9, 6, 2, 0, 0, 3, 0, 0, …
$ denial_reason_name_1           <chr> "", "", "", "", "", "", "", "", "", "", "Debt-to-income rati…
$ denial_reason_1                <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 1, NA, NA, NA, NA, N…
$ denial_reason_name_2           <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ denial_reason_2                <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ denial_reason_name_3           <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ denial_reason_3                <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ rate_spread                    <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ hoepa_status_name              <chr> "Not a HOEPA loan", "Not a HOEPA loan", "Not a HOEPA loan", …
$ hoepa_status                   <int> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, …
$ lien_status_name               <chr> "Secured by a first lien", "Not applicable", "Secured by a f…
$ lien_status                    <int> 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ edit_status_name               <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", …
$ edit_status                    <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ sequence_number                <int> 1107, 26374, 47400, 34456, 532, 78097, 27940, 441, 549245, 1…
$ population                     <int> 6278, 2173, 4094, 7744, 3990, 5780, 3229, 3601, 5295, 7524, …
$ minority_population            <dbl> 2.39, 5.57, 18.73, 7.98, 98.72, 6.83, 17.68, 11.41, 2.70, 4.…
$ hud_median_family_income       <int> 70600, 70600, 99500, 70300, 55400, 68500, 70600, 70600, 5600…
$ tract_to_msamd_income          <dbl> 100.54, 97.86, 77.68, 128.35, 77.03, 105.63, 106.13, 207.23,…
$ number_of_owner_occupied_units <int> 2272, 812, 1091, 2482, 899, 1894, 1011, 1207, 2189, 2437, 13…
$ number_of_1_to_4_family_units  <int> 2722, 934, 1331, 2742, 1825, 2394, 1203, 1300, 2493, 2821, 1…
$ application_date_indicator     <int> 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …

Different Type of Loans

We know that there are different types of loans. Lets see how is their distrubution.

ggplot(data = summarise_at(group_by(hmda_data_pa_df,loan_type_name),vars(loan_type),funs(n())),aes(x = loan_type_name,y =  loan_type)) + geom_bar(stat = "identity",fill = "#009E73") + geom_text(aes(label = loan_type), vjust = -0.5) +labs(title = "Type of Loans Distribution" , x = "Loan Type" , y = "Count")

Its pretty clear that conventinal type of loans recieve most applications. This is even the focus of this project. ### Filter out conventional loans. Then we print column names for the data.

# Filter to include conventional loans only.
hmda_data_pa_df <- hmda_data_pa_df[hmda_data_pa_df$loan_type == "1", ]

Data Analysis Section 3. Check for missing values in the dataset

Now, lets look at the missing values that are present in our data. We go through this in 4 steps. First we look for any NAs, then empty string, NULL values and at last we look for missing values encoded as “?”

writeLines("Checking for missing values with NA")
Checking for missing values with NA
sapply(hmda_data_pa_df, function(x) sum(is.na(x)))
                    as_of_year                  respondent_id                    agency_name 
                             0                              0                              0 
                   agency_abbr                    agency_code                 loan_type_name 
                             0                              0                              0 
                     loan_type             property_type_name                  property_type 
                             0                              0                              0 
             loan_purpose_name                   loan_purpose           owner_occupancy_name 
                             0                              0                              0 
               owner_occupancy               loan_amount_000s               preapproval_name 
                             0                              0                              0 
                   preapproval              action_taken_name                   action_taken 
                             0                              0                              0 
                    msamd_name                          msamd                     state_name 
                             0                          30529                              0 
                    state_abbr                     state_code                    county_name 
                             0                              0                              0 
                   county_code            census_tract_number       applicant_ethnicity_name 
                           336                            612                              0 
           applicant_ethnicity    co_applicant_ethnicity_name         co_applicant_ethnicity 
                             0                              0                              0 
         applicant_race_name_1               applicant_race_1          applicant_race_name_2 
                             0                              0                              0 
              applicant_race_2          applicant_race_name_3               applicant_race_3 
                        366359                              0                         367474 
         applicant_race_name_4               applicant_race_4          applicant_race_name_5 
                             0                         367548                              0 
              applicant_race_5       co_applicant_race_name_1            co_applicant_race_1 
                        367559                              0                              0 
      co_applicant_race_name_2            co_applicant_race_2       co_applicant_race_name_3 
                             0                         367178                              0 
           co_applicant_race_3       co_applicant_race_name_4            co_applicant_race_4 
                        367545                              0                         367563 
      co_applicant_race_name_5            co_applicant_race_5             applicant_sex_name 
                             0                         367565                              0 
                 applicant_sex          co_applicant_sex_name               co_applicant_sex 
                             0                              0                              0 
         applicant_income_000s            purchaser_type_name                 purchaser_type 
                         23012                              0                              0 
          denial_reason_name_1                denial_reason_1           denial_reason_name_2 
                             0                         323176                              0 
               denial_reason_2           denial_reason_name_3                denial_reason_3 
                        357862                              0                         366319 
                   rate_spread              hoepa_status_name                   hoepa_status 
                        361282                              0                              0 
              lien_status_name                    lien_status               edit_status_name 
                             0                              0                              0 
                   edit_status                sequence_number                     population 
                        315840                              0                            614 
           minority_population       hud_median_family_income          tract_to_msamd_income 
                           616                            612                            677 
number_of_owner_occupied_units  number_of_1_to_4_family_units     application_date_indicator 
                           630                            625                              0 
writeLines("Checking for missing values with empty strings")
Checking for missing values with empty strings
sapply(hmda_data_pa_df, function(x) sum(x == ""))
                    as_of_year                  respondent_id                    agency_name 
                             0                              0                              0 
                   agency_abbr                    agency_code                 loan_type_name 
                             0                              0                              0 
                     loan_type             property_type_name                  property_type 
                             0                              0                              0 
             loan_purpose_name                   loan_purpose           owner_occupancy_name 
                             0                              0                              0 
               owner_occupancy               loan_amount_000s               preapproval_name 
                             0                              0                              0 
                   preapproval              action_taken_name                   action_taken 
                             0                              0                              0 
                    msamd_name                          msamd                     state_name 
                         30529                             NA                              0 
                    state_abbr                     state_code                    county_name 
                             0                              0                            336 
                   county_code            census_tract_number       applicant_ethnicity_name 
                            NA                             NA                              0 
           applicant_ethnicity    co_applicant_ethnicity_name         co_applicant_ethnicity 
                             0                              0                              0 
         applicant_race_name_1               applicant_race_1          applicant_race_name_2 
                             0                              0                         366359 
              applicant_race_2          applicant_race_name_3               applicant_race_3 
                            NA                         367474                             NA 
         applicant_race_name_4               applicant_race_4          applicant_race_name_5 
                        367548                             NA                         367559 
              applicant_race_5       co_applicant_race_name_1            co_applicant_race_1 
                            NA                              0                              0 
      co_applicant_race_name_2            co_applicant_race_2       co_applicant_race_name_3 
                        367178                             NA                         367545 
           co_applicant_race_3       co_applicant_race_name_4            co_applicant_race_4 
                            NA                         367563                             NA 
      co_applicant_race_name_5            co_applicant_race_5             applicant_sex_name 
                        367565                             NA                              0 
                 applicant_sex          co_applicant_sex_name               co_applicant_sex 
                             0                              0                              0 
         applicant_income_000s            purchaser_type_name                 purchaser_type 
                            NA                              0                              0 
          denial_reason_name_1                denial_reason_1           denial_reason_name_2 
                        323176                             NA                         357862 
               denial_reason_2           denial_reason_name_3                denial_reason_3 
                            NA                         366319                             NA 
                   rate_spread              hoepa_status_name                   hoepa_status 
                            NA                              0                              0 
              lien_status_name                    lien_status               edit_status_name 
                             0                              0                         315840 
                   edit_status                sequence_number                     population 
                            NA                              0                             NA 
           minority_population       hud_median_family_income          tract_to_msamd_income 
                            NA                             NA                             NA 
number_of_owner_occupied_units  number_of_1_to_4_family_units     application_date_indicator 
                            NA                             NA                              0 
writeLines("Checking for missing values with ?")
Checking for missing values with ?
sapply(hmda_data_pa_df, function(x) sum(x == "?"))
                    as_of_year                  respondent_id                    agency_name 
                             0                              0                              0 
                   agency_abbr                    agency_code                 loan_type_name 
                             0                              0                              0 
                     loan_type             property_type_name                  property_type 
                             0                              0                              0 
             loan_purpose_name                   loan_purpose           owner_occupancy_name 
                             0                              0                              0 
               owner_occupancy               loan_amount_000s               preapproval_name 
                             0                              0                              0 
                   preapproval              action_taken_name                   action_taken 
                             0                              0                              0 
                    msamd_name                          msamd                     state_name 
                             0                             NA                              0 
                    state_abbr                     state_code                    county_name 
                             0                              0                              0 
                   county_code            census_tract_number       applicant_ethnicity_name 
                            NA                             NA                              0 
           applicant_ethnicity    co_applicant_ethnicity_name         co_applicant_ethnicity 
                             0                              0                              0 
         applicant_race_name_1               applicant_race_1          applicant_race_name_2 
                             0                              0                              0 
              applicant_race_2          applicant_race_name_3               applicant_race_3 
                            NA                              0                             NA 
         applicant_race_name_4               applicant_race_4          applicant_race_name_5 
                             0                             NA                              0 
              applicant_race_5       co_applicant_race_name_1            co_applicant_race_1 
                            NA                              0                              0 
      co_applicant_race_name_2            co_applicant_race_2       co_applicant_race_name_3 
                             0                             NA                              0 
           co_applicant_race_3       co_applicant_race_name_4            co_applicant_race_4 
                            NA                              0                             NA 
      co_applicant_race_name_5            co_applicant_race_5             applicant_sex_name 
                             0                             NA                              0 
                 applicant_sex          co_applicant_sex_name               co_applicant_sex 
                             0                              0                              0 
         applicant_income_000s            purchaser_type_name                 purchaser_type 
                            NA                              0                              0 
          denial_reason_name_1                denial_reason_1           denial_reason_name_2 
                             0                             NA                              0 
               denial_reason_2           denial_reason_name_3                denial_reason_3 
                            NA                              0                             NA 
                   rate_spread              hoepa_status_name                   hoepa_status 
                            NA                              0                              0 
              lien_status_name                    lien_status               edit_status_name 
                             0                              0                              0 
                   edit_status                sequence_number                     population 
                            NA                              0                             NA 
           minority_population       hud_median_family_income          tract_to_msamd_income 
                            NA                             NA                             NA 
number_of_owner_occupied_units  number_of_1_to_4_family_units     application_date_indicator 
                            NA                             NA                              0 
writeLines("Checking for missing values with null")
Checking for missing values with null
sapply(hmda_data_pa_df, function(x) sum(x == NULL))
                    as_of_year                  respondent_id                    agency_name 
                             0                              0                              0 
                   agency_abbr                    agency_code                 loan_type_name 
                             0                              0                              0 
                     loan_type             property_type_name                  property_type 
                             0                              0                              0 
             loan_purpose_name                   loan_purpose           owner_occupancy_name 
                             0                              0                              0 
               owner_occupancy               loan_amount_000s               preapproval_name 
                             0                              0                              0 
                   preapproval              action_taken_name                   action_taken 
                             0                              0                              0 
                    msamd_name                          msamd                     state_name 
                             0                              0                              0 
                    state_abbr                     state_code                    county_name 
                             0                              0                              0 
                   county_code            census_tract_number       applicant_ethnicity_name 
                             0                              0                              0 
           applicant_ethnicity    co_applicant_ethnicity_name         co_applicant_ethnicity 
                             0                              0                              0 
         applicant_race_name_1               applicant_race_1          applicant_race_name_2 
                             0                              0                              0 
              applicant_race_2          applicant_race_name_3               applicant_race_3 
                             0                              0                              0 
         applicant_race_name_4               applicant_race_4          applicant_race_name_5 
                             0                              0                              0 
              applicant_race_5       co_applicant_race_name_1            co_applicant_race_1 
                             0                              0                              0 
      co_applicant_race_name_2            co_applicant_race_2       co_applicant_race_name_3 
                             0                              0                              0 
           co_applicant_race_3       co_applicant_race_name_4            co_applicant_race_4 
                             0                              0                              0 
      co_applicant_race_name_5            co_applicant_race_5             applicant_sex_name 
                             0                              0                              0 
                 applicant_sex          co_applicant_sex_name               co_applicant_sex 
                             0                              0                              0 
         applicant_income_000s            purchaser_type_name                 purchaser_type 
                             0                              0                              0 
          denial_reason_name_1                denial_reason_1           denial_reason_name_2 
                             0                              0                              0 
               denial_reason_2           denial_reason_name_3                denial_reason_3 
                             0                              0                              0 
                   rate_spread              hoepa_status_name                   hoepa_status 
                             0                              0                              0 
              lien_status_name                    lien_status               edit_status_name 
                             0                              0                              0 
                   edit_status                sequence_number                     population 
                             0                              0                              0 
           minority_population       hud_median_family_income          tract_to_msamd_income 
                             0                              0                              0 
number_of_owner_occupied_units  number_of_1_to_4_family_units     application_date_indicator 
                             0                              0                              0 

Columns of Interest

First, we look at race and ethnicity columns and see what information they provide and how is the distribution per variable.

library(janitor)

writeLines("")
writeLines("Application ethnicity values")
Application ethnicity values
unique(hmda_data_pa_df$applicant_ethnicity_name)
[1] "Not Hispanic or Latino"                                                           
[2] "Hispanic or Latino"                                                               
[3] "Information not provided by applicant in mail, Internet, or telephone application"
[4] "Not applicable"                                                                   
writeLines("")
writeLines("Application race name 1 values")
Application race name 1 values
unique(hmda_data_pa_df$applicant_race_1)
[1] 5 6 7 4 3 2 1
unique(hmda_data_pa_df$applicant_race_name_1)
[1] "White"                                                                            
[2] "Information not provided by applicant in mail, Internet, or telephone application"
[3] "Not applicable"                                                                   
[4] "Native Hawaiian or Other Pacific Islander"                                        
[5] "Black or African American"                                                        
[6] "Asian"                                                                            
[7] "American Indian or Alaska Native"                                                 

Now, lets group the dataframe by ethnicity not Hispanic and print the count according to race.

grouped_by_race_info <- hmda_data_pa_df %>% filter(applicant_ethnicity_name == "Hispanic or Latino") %>%
  group_by(applicant_race_name_1) %>% 
           count() %>%
           ungroup() %>%
           replace(is.na(.), 0) %>% 
           adorn_totals(c("col")) %>% 
           arrange(-Total)

head(grouped_by_race_info)
                                                             applicant_race_name_1    n Total
                                                                             White 7711  7711
 Information not provided by applicant in mail, Internet, or telephone application 1438  1438
                                                         Black or African American  465   465
                                                  American Indian or Alaska Native  198   198
                                         Native Hawaiian or Other Pacific Islander  193   193
                                                                             Asian  106   106

Add a new column applicant_race_and_ethnicity and group all applicants with ethnicity as Hispanic or

Latino as Hispanic or Latino in this column.

For everyone else, this column gets values from the applicant_race_name_1 column

We do this because we want to merge these two columns into one and deal with it as one single predictor.

hmda_data_pa_df$applicant_race_and_ethnicity <- NA
hmda_data_pa_df$co_applicant_race_and_ethnicity <- NA

hmda_data_pa_df$applicant_race_and_ethnicity <- ifelse(hmda_data_pa_df$applicant_ethnicity_name == "Hispanic or Latino",
       "Hispanic or Latino", hmda_data_pa_df$applicant_race_name_1)

hmda_data_pa_df$co_applicant_race_and_ethnicity <- ifelse(hmda_data_pa_df$co_applicant_ethnicity_name == "Hispanic or Latino",
       "Hispanic or Latino", hmda_data_pa_df$co_applicant_race_name_1)

writeLines("")
writeLines("Unique values for the applicant_race_and_ethnicity column")
Unique values for the applicant_race_and_ethnicity column
writeLines("")
unique(hmda_data_pa_df$applicant_race_and_ethnicity)
[1] "White"                                                                            
[2] "Information not provided by applicant in mail, Internet, or telephone application"
[3] "Hispanic or Latino"                                                               
[4] "Not applicable"                                                                   
[5] "Native Hawaiian or Other Pacific Islander"                                        
[6] "Black or African American"                                                        
[7] "Asian"                                                                            
[8] "American Indian or Alaska Native"                                                 
head(hmda_data_pa_df)
NA

Graph mortgage distribution by applicant race and ethinicity.

See how the distroibution is for the loan application according to race and ethnicity. We summarise the count of application according to the applicants race.

mortgage_by_race_and_ethnicity = hmda_data_pa_df %>% group_by(applicant_race_and_ethnicity) %>%
  summarise(EthnicityCount = n()) %>%
  arrange(desc(EthnicityCount))

graph_by_enthicity(mortgage_by_race_and_ethnicity)

Graph which applicant races and ethnicities have the largest proportion of loans

in various stages. These include origination status, denied status, etc.

mortgage_status_by_race_and_ethnicity <- hmda_data_pa_df %>% group_by(action_taken_name, applicant_race_and_ethnicity) %>%
  summarise(ActionCount = n()) %>%
  arrange(desc(ActionCount))

mortgage_status_aggregated_by_race_and_ethnicity  = inner_join(mortgage_status_by_race_and_ethnicity, mortgage_by_race_and_ethnicity) %>% mutate(percentage = (ActionCount / EthnicityCount) * 100)
Joining, by = "applicant_race_and_ethnicity"
graph_application_race_proportion_of_loans(mortgage_status_aggregated_by_race_and_ethnicity)

Applicant income histograms.

Now lets see how the income distriubtion underlies for applicants. Lets see the median income for each category.

hmda_origination_status_df <- hmda_data_pa_df[hmda_data_pa_df$action_taken == "1", ]
graph_applicant_income_histogram(hmda_origination_status_df, "Applicant income distribution for originated loans")

Graph median income for originated loans.

Now lets see how the income distriubtion underlies for applicants. Lets see the median income for each category.

hmda_origination_status_df <- hmda_data_pa_df[hmda_data_pa_df$action_taken == "1", ]

head(hmda_origination_status_df)

hmda_origination_status_df %>% ggplot(aes(as.numeric(hud_median_family_income))) +
geom_histogram(binwidth = 1000,, fill=c("blue")) + labs(x = "Median Income", y = "Applicant Count", title = "Median Income Distribution for Area for Originated Loans") + theme_bw()

We see that Asians have the largest median income value amongst all. At the bottom, we have African Americans and Hispanic or Latino

Graph loan distribution by county.

mortgage_distribution_by_counties <- hmda_data_pa_df %>%
  filter(!is.na(county_name)) %>%
  group_by(county_name) %>%
  summarise(CountLoans = n() ) %>%
  mutate(percentage = ( CountLoans / sum(CountLoans) ) * 100 ) %>%
  mutate(county_name = reorder(county_name, percentage)) %>%
  arrange(desc(percentage)) %>%
  head(20)

graph_distribution_by_county(mortgage_distribution_by_counties)


originated_mortgage_distribution_by_counties <- hmda_origination_status_df %>%
  filter(!is.na(county_name)) %>%
  group_by(county_name) %>%
  summarise(CountLoans = n() ) %>%
  mutate(percentage = ( CountLoans / sum(CountLoans) ) *100 ) %>%
  mutate(county_name = reorder(county_name, percentage)) %>%
  arrange(desc(percentage)) %>%
  head(20)

graph_distribution_by_county(originated_mortgage_distribution_by_counties)

Graph home loan application distribution for the top 4 counties in the above

chart by applicant_race_1


county_names <- c("Allegheny County", "Philadelphia County", "Montgomery County", "Bucks County")

for (county_name in county_names) {
  
  hmda_data_county_df <- hmda_data_pa_df[hmda_data_pa_df$county_name == county_name, ]
  
  mortgage_by_race_county <- hmda_data_county_df %>% group_by(applicant_race_name_1) %>%
    summarise(RaceCount = n()) %>% arrange(desc(RaceCount))
  
  print(graph_mortgage_distribution_by_race1(mortgage_by_race_county))
}

Graph income distribution for Whites and African Americans per county for the

top 4 counties above.


for (county_name in county_names) {
  hmda_origination_status_df_by_county_white <- hmda_data_pa_df[hmda_data_pa_df$action_taken == "1" & hmda_data_pa_df$county_name == county_name & hmda_data_pa_df$applicant_race_name_1 == "White", ]
  print(graph_applicant_income_histogram(hmda_origination_status_df_by_county_white, "Income distribution for Whites"))

  hmda_origination_status_df_by_county_african_american <- hmda_data_pa_df[hmda_data_pa_df$action_taken == "1" & hmda_data_pa_df$county_name == county_name & hmda_data_pa_df$applicant_race_name_1 == "Black or African American", ]
  print(graph_applicant_income_histogram(hmda_origination_status_df_by_county_african_american, "Income distribution for African Americans"))
}

Graph home loan application distribution for the top 4 counties in the above

chart by applicant_race_and_ethnicity


county_names <- c("Allegheny County", "Philadelphia County", "Montgomery County", "Bucks County")

for (county_name in county_names) {
  
  hmda_data_county_df <- hmda_data_pa_df[hmda_data_pa_df$county_name == county_name, ]
  
  mortgage_by_race_county <- hmda_data_county_df %>% group_by(applicant_race_and_ethnicity) %>%
    summarise(RaceCount = n()) %>% arrange(desc(RaceCount))
  
  print(graph_mortgage_distribution_by_race_and_ethnicity(mortgage_by_race_county))
}

Graph which communities have the largest proportion of loans in various stages.

for the top 4 counties listed above.

These include origination status, denied status, etc.


for (county_name in county_names) {
  
  hmda_data_county_df <- hmda_data_pa_df[hmda_data_pa_df$county_name == county_name, ]
  
  mortgage_by_race1_county <- hmda_data_county_df %>% group_by(applicant_race_and_ethnicity) %>%
    summarise(RaceCount = n()) %>% arrange(desc(RaceCount))

  mortgage_status_by_race1_by_county <- hmda_data_county_df %>% group_by(action_taken_name, applicant_race_and_ethnicity) %>%
    summarise(ActionCount = n()) %>%
    arrange(desc(ActionCount))
  
  mortgage_status_aggregated_by_race1_by_county  = inner_join(mortgage_status_by_race1_by_county, mortgage_by_race1_county) %>% mutate(percentage = (ActionCount / RaceCount) * 100)
  
  print(graph_application_race_and_ethnicity_proportion_of_loans(mortgage_status_aggregated_by_race1_by_county))
}
Joining, by = "applicant_race_and_ethnicity"

Visualize missing variables

Now we start looking at the missing values and see how can we deal with them .So here, we try and vizualize the missing values


visualize_missing_values(hmda_data_pa_df)

In this graph, we see the missing value count for each column and for each category too. There are alot of missing in some columns like co applicant and applicant 2-3-4 race.

Now we try to impute the missing values. Easy way out here is to impute it with mice function. Its not the best but initially we go with this and see how it performs. # Impute as needed.

Impute as needed.


# https://www.rdocumentation.org/packages/mice/versions/3.8.0/topics/mice.impute.cart
hmda_data_pa_df_imputed <- mice(hmda_data_pa_df, m=1, maxit=2, meth='cart',seed=500)

hmda_data_pa_df_imputed <- mice::complete(hmda_data_pa_df_imputed)

More analysis on the imputed dataset.


summary(hmda_data_pa_df_imputed)

gg_miss_upset(hmda_data_pa_df_imputed)

Additional analysis on the hmda dataset. correlation matrix, plots, etc.

# https://stackoverflow.com/questions/20637360/convert-all-data-frame-character-columns-to-factors
hmda_data_pa_df$loan_to_income_ratio <- hmda_data_pa_df$loan_amount_000s / hmda_data_pa_df$applicant_income_000s

hmda_data_pa_df[sapply(hmda_data_pa_df, is.character)] <- lapply(hmda_data_pa_df[sapply(hmda_data_pa_df, is.character)], 
                                       as.factor)

hmda_data_pa_df_for_correlation <- as.data.frame(lapply(hmda_data_pa_df, as.integer))

#head(hmda_data_pa_df_for_correlation[, c("applicant_income_000s", "loan_amount_000s")])

head(hmda_data_pa_df_for_correlation)

corr_simple(hmda_data_pa_df_for_correlation)

corrplot(cor(hmda_data_pa_df_for_correlation[, c("applicant_income_000s", "loan_amount_000s")], use = "na.or.complete"))

Additional analysis on the hmda imputed dataset. correlation plots, etc.

# hmda_data_pa_df_imputed <- hmda_data_pa_df;
# https://stackoverflow.com/questions/20637360/convert-all-data-frame-character-columns-to-factors
hmda_data_pa_df_imputed$loan_to_income_ratio <- hmda_data_pa_df_imputed$loan_amount_000s / hmda_data_pa_df_imputed$applicant_income_000s

hmda_data_pa_df_imputed[sapply(hmda_data_pa_df_imputed, is.character)] <- lapply(hmda_data_pa_df_imputed[sapply(hmda_data_pa_df_imputed, is.character)], 
                                       as.factor)

hmda_data_pa_df_imputed_for_correlation <- as.data.frame(lapply(hmda_data_pa_df_imputed, as.integer))

head(hmda_data_pa_df_imputed_for_correlation[, c("applicant_income_000s", "loan_amount_000s")])

corr_simple(hmda_data_pa_df_imputed_for_correlation)

corrplot(cor(hmda_data_pa_df_imputed_for_correlation[, c("applicant_income_000s", "loan_amount_000s")], use = "na.or.complete"))

Relationship between race and approved loans.


hmda_model_df <- hmda_data_frame_for_model(hmda_data_pa_df_imputed)
hmda_model_df <- process_model_df_columns(hmda_model_df)

l <- ggplot(hmda_model_df, aes(applicant_race_and_ethnicity,fill = loan_granted))
l <- l + geom_histogram(stat="count") + coord_flip()
print(l)

Relationship between loan purpose and loan action.

l <- ggplot(hmda_model_df, aes(loan_purpose, fill = loan_granted))
l <- l + geom_histogram(stat="count") + coord_flip()
print(l)
plot(hmda_model_df$loan_granted, main="Loan granted Variable",
     col=colors()[100:102],
     xlab="Loan distribution")

Applicants loan amount

skew <- paste("Skewness:",skewness(hmda_model_df$loan_amount_000s,na.rm = TRUE))
ggplot(data = hmda_model_df , aes(x = loan_amount_000s)) + geom_histogram(fill = "steelblue") + labs(title = "Loan amount distribution" , x = "Loan amount in thousands" , y = "Count")+ annotate("text", x = 100000, y = 300000, size = 3.2,label = skew)

Looks like the data is highly skewed.

#install.packages("moments")
library(moments)
skewness(hmda_model_df$loan_amount_000s,na.rm = TRUE)

The data for loan amount is highly right skewed. Changes should be made so that the prediction model does not mess up.

Handling highly skewed data. Log Transformation

skew <- paste("Skewness:",skewness(log(hmda_model_df$loan_amount_000s),na.rm = TRUE))
ggplot(data = hmda_model_df , aes(x = log(loan_amount_000s))) + geom_histogram(fill = "steelblue") + labs(title = "Log transformed distribution for Loan amount" , x = "log(Loan Amount)", y = 'Count')+ annotate("text", x = 8, y = 100000, size = 3.2,label = skew)
skewness(log(hmda_model_df$loan_amount_000s),na.rm = TRUE)

Boxplot of log of loan amounts.

boxplot(log(hmda_model_df$loan_amount_000s),col = colors()[100:109],
        main = "Boxplot of Log of Loan Amounts",
        xlab="Loan Amount",
        ylab="Distribution of Log of Loan Amounts")

Same is the case with applicants income

skew <- paste("Skewness:",skewness(hmda_model_df$applicant_income_000s,na.rm = TRUE))
ggplot(data = hmda_model_df , aes(x = applicant_income_000s)) + geom_histogram(fill = "steelblue") + labs(title = "Applicant Income distribution" , x = "Applicant Income in thousands" , y = "Count") + annotate("text", x = 100000, y = 90000, size = 3.2,label = skew)
skew <- paste("Skewness:",skewness(log(hmda_model_df$applicant_income_000s),na.rm=TRUE))
ggplot(data = hmda_model_df , aes(x = log(applicant_income_000s))) + geom_histogram(fill = "steelblue") + labs(title = "Log transformed distribution for Applicant Income" , x = "log(Applicant Income)", y = 'Count') +annotate("text", x = 10, y = 90000, size = 3.2,label = skew)

Box plots for log of loan amounts vs decision


boxplot(log(loan_amount_000s)~loan_granted, xlab="Loan decision",ylab="Log of Loan Amounts",col=c("pink","lightblue"),
        main="Exploratory Data Analysis Plot\n of Loan Decision Versus Log of Loan Amounts", data = hmda_model_df)

Box plots for log of loan amounts vs decision


boxplot(log(applicant_income_000s)~loan_granted, xlab="Loan decision",ylab="Log of Applicant Income",col=c("pink","lightblue"),
        main="Exploratory Data Analysis Plot\n of Loan Decision Versus Log of Applicant Income", data = hmda_model_df)

Plot for log of applicant income, race and ethnicity with color by loan decision.

ggplot(hmda_model_df, aes(log(applicant_income_000s), applicant_race_and_ethnicity, color = loan_granted)) + 
  geom_jitter() +
  ggtitle("Log of Applicant income vs. Applicant race and ethnicity , by  color = Loan decision") +
  theme_light()

Plot for log of loan amounts, race and ethnicity with color by loan decision.

ggplot(hmda_model_df, aes(log(loan_amount_000s), applicant_race_and_ethnicity, color = loan_granted)) + 
  geom_jitter() +
  ggtitle("Log of loan amount vs. Applicant race and ethnicity , by  color = Loan decision") +
  theme_light()

Plot for loan to income ratio, race and ethnicity with color by loan decision.


ggplot(hmda_model_df, aes(loan_to_income_ratio, applicant_race_and_ethnicity, color = loan_granted)) + 
  geom_jitter() +
  ggtitle("Loan to Income ratio vs. Applicant race and ethnicity , by  color = Loan decision") +
  theme_light()

Save the imputed dataframe to file.


write.csv(hmda_data_pa_df_imputed, paste(data_dir, "/2016/hmda_2016_pa_imputed.csv", sep = ""), row.names = FALSE)
LS0tCnRpdGxlOiAiUGVubnN5bHZhbmlhIDIwMTYgSE1EQSBFREEiCmF1dGhvcjogIkFuYW50YW5hcmF5YW5hbiBHIEl5ZW5nYXIsIE9ta2FyIFBhd2FyIFtQbGVhc2UgYWRkIHlvdXIgbmFtZXMgaGVyZV0iIApkYXRlOiAiNC82LzIwMjAiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgpIZXJlIHdlIGRvIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgb24gSERNQSBkYXRhIG9idGFpbmVkIGZvciBQZW5uc3lsdmFuaWEgaW4gdGhlIHllYXIgMjAxNi4gV2Ugd2lsbCBzdGFydCBmcm9tIGxvb2tpbmcgYXQgdGhlIGRhdGEgc3VwZXJmaWNpYWxseSBhbmQgdGhlbiBkaXZpbmcgaW50byBjb2x1bW5zIG9mIGludGVyZXN0LiBUaGVuIHdlIHNlZSBmb3IgYW55IG1pc3NpbmcgdmFsdWVzIGFuZCBoYW5kbGUgdGhlbS4gTGV0cyBnZXQgc3RhcnRlZCB3aXRoIHRoZSBzdGVwcy4gIyMgR2xvYmFsIHNldHVwIGxpa2Ugd29ya2luZyBkaXJlY3RvcnksIGRhdGEgZGlyZWN0b3J5IGV0YyBzaG91bGQgaGFwcGVuIGhlcmUuIEdsb2JhbCBzZXR1cCBsaWtlIHdvcmtpbmcgZGlyZWN0b3J5LCBkYXRhIGRpcmVjdG9yeSBldGMgc2hvdWxkIGhhcHBlbiBoZXJlLgoKYGBge3J9CmxpYnJhcnkoc3lzKQp3b3JraW5nX2RpcmVjdG9yeSA8LSBnZXR3ZCgpCgpzZXR3ZChkaXJuYW1lKGRpcm5hbWUod29ya2luZ19kaXJlY3RvcnkpKSkKCndyaXRlTGluZXMoIiIpCmdldHdkKCkKCmRhdGFfZGlyIDwtICIvVXNlcnMvb21rYXJwYXdhci9EZXNrdG9wL0RhdGEvUEEvIgoKYGBgCgojIyBJbnN0YWxsIHJlcXVpcmVkIHBhY2thZ2VzLgpgYGB7cn0KIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy80MDkwMTY5L2VsZWdhbnQtd2F5LXRvLWNoZWNrLWZvci1taXNzaW5nLXBhY2thZ2VzLWFuZC1pbnN0YWxsLXRoZW0KbGlzdF9vZl9wYWNrYWdlcyA8LSBjKCJtbGJlbmNoIiwgImNvcnJwbG90IiwgInJ2ZXN0IiwgInRpZHlyIiwgInN0cmluZ3IiLCAiZHBseXIiLCAibHVicmlkYXRlIiwgImRhdGEudGFibGUiLCAibWljZSIsICJzY2FsZXMiLCAibmFuaWFyIiwgInJwYXJ0IiwgInJwYXJ0LnBsb3QiLCAiY2FyZXQiKQpuZXcucGFja2FnZXMgPC0gbGlzdF9vZl9wYWNrYWdlc1shKGxpc3Rfb2ZfcGFja2FnZXMgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssIlBhY2thZ2UiXSldCgppZiAobGVuZ3RoKG5ldy5wYWNrYWdlcykpIHsKICBwcmludCgiSW5zdGFsbGluZyBwYWNrYWdlc1xuIikKICBpbnN0YWxsLnBhY2thZ2VzKG5ldy5wYWNrYWdlcygpKQp9CgpsaWJyYXJ5KGNvcnJwbG90KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShkcGx5cikKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KG1pY2UpCmxpYnJhcnkocnN0dWRpb2FwaSkgICAgCmxpYnJhcnkobmFuaWFyKQoKc291cmNlKHBhc3RlKGRpcm5hbWUoZGlybmFtZShkaXJuYW1lKHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGgpKSksICJ1dGlscy91dGlscy5yIiwgc2VwPSIvIikpCnNvdXJjZShwYXN0ZShkaXJuYW1lKGRpcm5hbWUoZGlybmFtZShyc3R1ZGlvYXBpOjpnZXRBY3RpdmVEb2N1bWVudENvbnRleHQoKSRwYXRoKSkpLCAidXRpbHMvbW9kZWxfdXRpbHMuciIsIHNlcD0iLyIpKQpgYGAKCiMjIExvYWQgZGF0YSBmaWxlLgpgYGB7cn0KaG1kYV9kYXRhX3BhIDwtIGZyZWFkKHBhc3RlKGRhdGFfZGlyLCAiaG1kYV8yMDE2X3BhX2FsbC1yZWNvcmRzX2xhYmVscy5jc3YiLCBzZXAgPSAiIikpCmBgYAoKIyMgRGF0YSBhbmFseXNpcyBzZWN0aW9uIDEuIApMZXRzIHNlZSBmaXJzdCBmZXcgcm93cyBvZiBvdXIgZGF0YSBhbmQgd2hhdCB0aGV5IHRlbGwgYWJvdXQgdGhlIGFwcGxpY2F0aW9uLgpgYGB7cn0KaG1kYV9kYXRhX3BhX2RmIDwtIGFzLmRhdGEuZnJhbWUoaG1kYV9kYXRhX3BhKQoKY29sbmFtZXMoaG1kYV9kYXRhX3BhX2RmKQoKd3JpdGVMaW5lcygiIikKCmhlYWQoaG1kYV9kYXRhX3BhX2RmLCAxMCkKCmBgYAoKIyMgRGF0YSBhbmFseXNpcyBzZWN0aW9uIDIuIFByaW50IGdsaW1wc2Ugb2YgZGF0YXNldCBpLmUgYSB2ZXJ0aWNhbCBwcmV2aWV3IG9mIHRoZSBkYXRhc2V0LgpgYGB7cn0KZGltKGhtZGFfZGF0YV9wYV9kZikKd3JpdGVMaW5lcygiR2xpbXBzZSBvZiBobWRhIGRhdGFzZXQgZm9yIFBBIikKZ2xpbXBzZShobWRhX2RhdGFfcGFfZGYpCgpgYGAKIyBEaWZmZXJlbnQgVHlwZSBvZiBMb2FucwpXZSBrbm93IHRoYXQgdGhlcmUgYXJlIGRpZmZlcmVudCB0eXBlcyBvZiBsb2Fucy4gTGV0cyBzZWUgaG93IGlzIHRoZWlyIGRpc3RydWJ1dGlvbi4KYGBge3J9CmdncGxvdChkYXRhID0gc3VtbWFyaXNlX2F0KGdyb3VwX2J5KGhtZGFfZGF0YV9wYV9kZixsb2FuX3R5cGVfbmFtZSksdmFycyhsb2FuX3R5cGUpLGZ1bnMobigpKSksYWVzKHggPSBsb2FuX3R5cGVfbmFtZSx5ID0gIGxvYW5fdHlwZSkpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsZmlsbCA9ICIjMDA5RTczIikgKyBnZW9tX3RleHQoYWVzKGxhYmVsID0gbG9hbl90eXBlKSwgdmp1c3QgPSAtMC41KSArbGFicyh0aXRsZSA9ICJUeXBlIG9mIExvYW5zIERpc3RyaWJ1dGlvbiIgLCB4ID0gIkxvYW4gVHlwZSIgLCB5ID0gIkNvdW50IikKYGBgCkl0cyBwcmV0dHkgY2xlYXIgdGhhdCBjb252ZW50aW5hbCB0eXBlIG9mIGxvYW5zIHJlY2lldmUgbW9zdCBhcHBsaWNhdGlvbnMuIFRoaXMgaXMgZXZlbiB0aGUgZm9jdXMgb2YgdGhpcyBwcm9qZWN0LiAKIyMjIEZpbHRlciBvdXQgY29udmVudGlvbmFsIGxvYW5zLiBUaGVuIHdlIHByaW50IGNvbHVtbiBuYW1lcyBmb3IgdGhlIGRhdGEuCmBgYHtyfQojIEZpbHRlciB0byBpbmNsdWRlIGNvbnZlbnRpb25hbCBsb2FucyBvbmx5LgpobWRhX2RhdGFfcGFfZGYgPC0gaG1kYV9kYXRhX3BhX2RmW2htZGFfZGF0YV9wYV9kZiRsb2FuX3R5cGUgPT0gIjEiLCBdCmBgYAoKIyMgRGF0YSBBbmFseXNpcyBTZWN0aW9uIDMuIENoZWNrIGZvciBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgZGF0YXNldApOb3csIGxldHMgbG9vayBhdCB0aGUgbWlzc2luZyB2YWx1ZXMgdGhhdCBhcmUgcHJlc2VudCBpbiBvdXIgZGF0YS4gV2UgZ28gdGhyb3VnaCB0aGlzIGluIDQgc3RlcHMuIEZpcnN0IHdlIGxvb2sgZm9yIGFueSBOQXMsIHRoZW4gZW1wdHkgc3RyaW5nLCBOVUxMIHZhbHVlcyBhbmQgYXQgbGFzdCB3ZSBsb29rIGZvciBtaXNzaW5nIHZhbHVlcyBlbmNvZGVkIGFzIOKAnD/igJ0KYGBge3J9CndyaXRlTGluZXMoIkNoZWNraW5nIGZvciBtaXNzaW5nIHZhbHVlcyB3aXRoIE5BIikKc2FwcGx5KGhtZGFfZGF0YV9wYV9kZiwgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKCndyaXRlTGluZXMoIkNoZWNraW5nIGZvciBtaXNzaW5nIHZhbHVlcyB3aXRoIGVtcHR5IHN0cmluZ3MiKQpzYXBwbHkoaG1kYV9kYXRhX3BhX2RmLCBmdW5jdGlvbih4KSBzdW0oeCA9PSAiIikpCgp3cml0ZUxpbmVzKCJDaGVja2luZyBmb3IgbWlzc2luZyB2YWx1ZXMgd2l0aCA/IikKc2FwcGx5KGhtZGFfZGF0YV9wYV9kZiwgZnVuY3Rpb24oeCkgc3VtKHggPT0gIj8iKSkKCndyaXRlTGluZXMoIkNoZWNraW5nIGZvciBtaXNzaW5nIHZhbHVlcyB3aXRoIG51bGwiKQpzYXBwbHkoaG1kYV9kYXRhX3BhX2RmLCBmdW5jdGlvbih4KSBzdW0oeCA9PSBOVUxMKSkKCmBgYAoKIyMgQ29sdW1ucyBvZiBJbnRlcmVzdApGaXJzdCwgd2UgbG9vayBhdCByYWNlIGFuZCBldGhuaWNpdHkgY29sdW1ucyBhbmQgc2VlIHdoYXQgaW5mb3JtYXRpb24gdGhleSBwcm92aWRlIGFuZCBob3cgaXMgdGhlIGRpc3RyaWJ1dGlvbiBwZXIgdmFyaWFibGUuCmBgYHtyfQpsaWJyYXJ5KGphbml0b3IpCgp3cml0ZUxpbmVzKCIiKQp3cml0ZUxpbmVzKCJBcHBsaWNhdGlvbiBldGhuaWNpdHkgdmFsdWVzIikKdW5pcXVlKGhtZGFfZGF0YV9wYV9kZiRhcHBsaWNhbnRfZXRobmljaXR5X25hbWUpCgp3cml0ZUxpbmVzKCIiKQp3cml0ZUxpbmVzKCJBcHBsaWNhdGlvbiByYWNlIG5hbWUgMSB2YWx1ZXMiKQp1bmlxdWUoaG1kYV9kYXRhX3BhX2RmJGFwcGxpY2FudF9yYWNlXzEpCnVuaXF1ZShobWRhX2RhdGFfcGFfZGYkYXBwbGljYW50X3JhY2VfbmFtZV8xKQoKYGBgCk5vdywgbGV0cyBncm91cCB0aGUgZGF0YWZyYW1lIGJ5IGV0aG5pY2l0eSBub3QgSGlzcGFuaWMgYW5kIHByaW50IHRoZSBjb3VudCBhY2NvcmRpbmcgdG8gcmFjZS4KYGBge3J9Cmdyb3VwZWRfYnlfcmFjZV9pbmZvIDwtIGhtZGFfZGF0YV9wYV9kZiAlPiUgZmlsdGVyKGFwcGxpY2FudF9ldGhuaWNpdHlfbmFtZSA9PSAiSGlzcGFuaWMgb3IgTGF0aW5vIikgJT4lCiAgZ3JvdXBfYnkoYXBwbGljYW50X3JhY2VfbmFtZV8xKSAlPiUgCiAgICAgICAgICAgY291bnQoKSAlPiUKICAgICAgICAgICB1bmdyb3VwKCkgJT4lCiAgICAgICAgICAgcmVwbGFjZShpcy5uYSguKSwgMCkgJT4lIAogICAgICAgICAgIGFkb3JuX3RvdGFscyhjKCJjb2wiKSkgJT4lIAogICAgICAgICAgIGFycmFuZ2UoLVRvdGFsKQoKaGVhZChncm91cGVkX2J5X3JhY2VfaW5mbykKYGBgCgojIyBBZGQgYSBuZXcgY29sdW1uIGFwcGxpY2FudF9yYWNlX2FuZF9ldGhuaWNpdHkgYW5kIGdyb3VwIGFsbCBhcHBsaWNhbnRzIHdpdGggZXRobmljaXR5IGFzIEhpc3BhbmljIG9yIAojIyBMYXRpbm8gYXMgSGlzcGFuaWMgb3IgTGF0aW5vIGluIHRoaXMgY29sdW1uLgojIyBGb3IgZXZlcnlvbmUgZWxzZSwgdGhpcyBjb2x1bW4gZ2V0cyB2YWx1ZXMgZnJvbSB0aGUgYXBwbGljYW50X3JhY2VfbmFtZV8xIGNvbHVtbgpXZSBkbyB0aGlzIGJlY2F1c2Ugd2Ugd2FudCB0byBtZXJnZSB0aGVzZSB0d28gY29sdW1ucyBpbnRvIG9uZSBhbmQgZGVhbCB3aXRoIGl0IGFzIG9uZSBzaW5nbGUgcHJlZGljdG9yLgpgYGB7cn0KaG1kYV9kYXRhX3BhX2RmJGFwcGxpY2FudF9yYWNlX2FuZF9ldGhuaWNpdHkgPC0gTkEKaG1kYV9kYXRhX3BhX2RmJGNvX2FwcGxpY2FudF9yYWNlX2FuZF9ldGhuaWNpdHkgPC0gTkEKCmhtZGFfZGF0YV9wYV9kZiRhcHBsaWNhbnRfcmFjZV9hbmRfZXRobmljaXR5IDwtIGlmZWxzZShobWRhX2RhdGFfcGFfZGYkYXBwbGljYW50X2V0aG5pY2l0eV9uYW1lID09ICJIaXNwYW5pYyBvciBMYXRpbm8iLAogICAgICAgIkhpc3BhbmljIG9yIExhdGlubyIsIGhtZGFfZGF0YV9wYV9kZiRhcHBsaWNhbnRfcmFjZV9uYW1lXzEpCgpobWRhX2RhdGFfcGFfZGYkY29fYXBwbGljYW50X3JhY2VfYW5kX2V0aG5pY2l0eSA8LSBpZmVsc2UoaG1kYV9kYXRhX3BhX2RmJGNvX2FwcGxpY2FudF9ldGhuaWNpdHlfbmFtZSA9PSAiSGlzcGFuaWMgb3IgTGF0aW5vIiwKICAgICAgICJIaXNwYW5pYyBvciBMYXRpbm8iLCBobWRhX2RhdGFfcGFfZGYkY29fYXBwbGljYW50X3JhY2VfbmFtZV8xKQoKd3JpdGVMaW5lcygiIikKd3JpdGVMaW5lcygiVW5pcXVlIHZhbHVlcyBmb3IgdGhlIGFwcGxpY2FudF9yYWNlX2FuZF9ldGhuaWNpdHkgY29sdW1uIikKd3JpdGVMaW5lcygiIikKdW5pcXVlKGhtZGFfZGF0YV9wYV9kZiRhcHBsaWNhbnRfcmFjZV9hbmRfZXRobmljaXR5KQoKaGVhZChobWRhX2RhdGFfcGFfZGYpCgpgYGAKCiMjIEdyYXBoIG1vcnRnYWdlIGRpc3RyaWJ1dGlvbiBieSBhcHBsaWNhbnQgcmFjZSBhbmQgZXRoaW5pY2l0eS4KU2VlIGhvdyB0aGUgZGlzdHJvaWJ1dGlvbiBpcyBmb3IgdGhlIGxvYW4gYXBwbGljYXRpb24gYWNjb3JkaW5nIHRvIHJhY2UgYW5kIGV0aG5pY2l0eS4gV2Ugc3VtbWFyaXNlIHRoZSBjb3VudCBvZiBhcHBsaWNhdGlvbiBhY2NvcmRpbmcgdG8gdGhlIGFwcGxpY2FudHMgcmFjZS4KYGBge3J9Cm1vcnRnYWdlX2J5X3JhY2VfYW5kX2V0aG5pY2l0eSA9IGhtZGFfZGF0YV9wYV9kZiAlPiUgZ3JvdXBfYnkoYXBwbGljYW50X3JhY2VfYW5kX2V0aG5pY2l0eSkgJT4lCiAgc3VtbWFyaXNlKEV0aG5pY2l0eUNvdW50ID0gbigpKSAlPiUKICBhcnJhbmdlKGRlc2MoRXRobmljaXR5Q291bnQpKQoKZ3JhcGhfYnlfZW50aGljaXR5KG1vcnRnYWdlX2J5X3JhY2VfYW5kX2V0aG5pY2l0eSkKCmBgYAoKIyBHcmFwaCB3aGljaCBhcHBsaWNhbnQgcmFjZXMgYW5kIGV0aG5pY2l0aWVzIGhhdmUgdGhlIGxhcmdlc3QgcHJvcG9ydGlvbiBvZiBsb2FucwojIGluIHZhcmlvdXMgc3RhZ2VzLiBUaGVzZSBpbmNsdWRlIG9yaWdpbmF0aW9uIHN0YXR1cywgZGVuaWVkIHN0YXR1cywgZXRjLgoKYGBge3J9Cm1vcnRnYWdlX3N0YXR1c19ieV9yYWNlX2FuZF9ldGhuaWNpdHkgPC0gaG1kYV9kYXRhX3BhX2RmICU+JSBncm91cF9ieShhY3Rpb25fdGFrZW5fbmFtZSwgYXBwbGljYW50X3JhY2VfYW5kX2V0aG5pY2l0eSkgJT4lCiAgc3VtbWFyaXNlKEFjdGlvbkNvdW50ID0gbigpKSAlPiUKICBhcnJhbmdlKGRlc2MoQWN0aW9uQ291bnQpKQoKbW9ydGdhZ2Vfc3RhdHVzX2FnZ3JlZ2F0ZWRfYnlfcmFjZV9hbmRfZXRobmljaXR5ICA9IGlubmVyX2pvaW4obW9ydGdhZ2Vfc3RhdHVzX2J5X3JhY2VfYW5kX2V0aG5pY2l0eSwgbW9ydGdhZ2VfYnlfcmFjZV9hbmRfZXRobmljaXR5KSAlPiUgbXV0YXRlKHBlcmNlbnRhZ2UgPSAoQWN0aW9uQ291bnQgLyBFdGhuaWNpdHlDb3VudCkgKiAxMDApCgpncmFwaF9hcHBsaWNhdGlvbl9yYWNlX3Byb3BvcnRpb25fb2ZfbG9hbnMobW9ydGdhZ2Vfc3RhdHVzX2FnZ3JlZ2F0ZWRfYnlfcmFjZV9hbmRfZXRobmljaXR5KQoKYGBgCgojIEFwcGxpY2FudCBpbmNvbWUgaGlzdG9ncmFtcy4KTm93IGxldHMgc2VlIGhvdyB0aGUgaW5jb21lIGRpc3RyaXVidGlvbiB1bmRlcmxpZXMgZm9yIGFwcGxpY2FudHMuIExldHMgc2VlIHRoZSBtZWRpYW4gaW5jb21lIGZvciBlYWNoIGNhdGVnb3J5LgpgYGB7cn0KaG1kYV9vcmlnaW5hdGlvbl9zdGF0dXNfZGYgPC0gaG1kYV9kYXRhX3BhX2RmW2htZGFfZGF0YV9wYV9kZiRhY3Rpb25fdGFrZW4gPT0gIjEiLCBdCmdyYXBoX2FwcGxpY2FudF9pbmNvbWVfaGlzdG9ncmFtKGhtZGFfb3JpZ2luYXRpb25fc3RhdHVzX2RmLCAiQXBwbGljYW50IGluY29tZSBkaXN0cmlidXRpb24gZm9yIG9yaWdpbmF0ZWQgbG9hbnMiKQpgYGAKCiMgR3JhcGggbWVkaWFuIGluY29tZSBmb3Igb3JpZ2luYXRlZCBsb2Fucy4KTm93IGxldHMgc2VlIGhvdyB0aGUgaW5jb21lIGRpc3RyaXVidGlvbiB1bmRlcmxpZXMgZm9yIGFwcGxpY2FudHMuIExldHMgc2VlIHRoZSBtZWRpYW4gaW5jb21lIGZvciBlYWNoIGNhdGVnb3J5LgpgYGB7cn0KaG1kYV9vcmlnaW5hdGlvbl9zdGF0dXNfZGYgPC0gaG1kYV9kYXRhX3BhX2RmW2htZGFfZGF0YV9wYV9kZiRhY3Rpb25fdGFrZW4gPT0gIjEiLCBdCgpoZWFkKGhtZGFfb3JpZ2luYXRpb25fc3RhdHVzX2RmKQoKaG1kYV9vcmlnaW5hdGlvbl9zdGF0dXNfZGYgJT4lIGdncGxvdChhZXMoYXMubnVtZXJpYyhodWRfbWVkaWFuX2ZhbWlseV9pbmNvbWUpKSkgKwpnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwMDAsLCBmaWxsPWMoImJsdWUiKSkgKyBsYWJzKHggPSAiTWVkaWFuIEluY29tZSIsIHkgPSAiQXBwbGljYW50IENvdW50IiwgdGl0bGUgPSAiTWVkaWFuIEluY29tZSBEaXN0cmlidXRpb24gZm9yIEFyZWEgZm9yIE9yaWdpbmF0ZWQgTG9hbnMiKSArIHRoZW1lX2J3KCkKYGBgCldlIHNlZSB0aGF0IEFzaWFucyBoYXZlIHRoZSBsYXJnZXN0IG1lZGlhbiBpbmNvbWUgdmFsdWUgYW1vbmdzdCBhbGwuIEF0IHRoZSBib3R0b20sIHdlIGhhdmUgQWZyaWNhbiBBbWVyaWNhbnMgYW5kIEhpc3BhbmljIG9yIExhdGlubwoKIyMgR3JhcGggbG9hbiBkaXN0cmlidXRpb24gYnkgY291bnR5LgoKYGBge3J9Cm1vcnRnYWdlX2Rpc3RyaWJ1dGlvbl9ieV9jb3VudGllcyA8LSBobWRhX2RhdGFfcGFfZGYgJT4lCiAgZmlsdGVyKCFpcy5uYShjb3VudHlfbmFtZSkpICU+JQogIGdyb3VwX2J5KGNvdW50eV9uYW1lKSAlPiUKICBzdW1tYXJpc2UoQ291bnRMb2FucyA9IG4oKSApICU+JQogIG11dGF0ZShwZXJjZW50YWdlID0gKCBDb3VudExvYW5zIC8gc3VtKENvdW50TG9hbnMpICkgKiAxMDAgKSAlPiUKICBtdXRhdGUoY291bnR5X25hbWUgPSByZW9yZGVyKGNvdW50eV9uYW1lLCBwZXJjZW50YWdlKSkgJT4lCiAgYXJyYW5nZShkZXNjKHBlcmNlbnRhZ2UpKSAlPiUKICBoZWFkKDIwKQoKZ3JhcGhfZGlzdHJpYnV0aW9uX2J5X2NvdW50eShtb3J0Z2FnZV9kaXN0cmlidXRpb25fYnlfY291bnRpZXMpCgpvcmlnaW5hdGVkX21vcnRnYWdlX2Rpc3RyaWJ1dGlvbl9ieV9jb3VudGllcyA8LSBobWRhX29yaWdpbmF0aW9uX3N0YXR1c19kZiAlPiUKICBmaWx0ZXIoIWlzLm5hKGNvdW50eV9uYW1lKSkgJT4lCiAgZ3JvdXBfYnkoY291bnR5X25hbWUpICU+JQogIHN1bW1hcmlzZShDb3VudExvYW5zID0gbigpICkgJT4lCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSAoIENvdW50TG9hbnMgLyBzdW0oQ291bnRMb2FucykgKSAqMTAwICkgJT4lCiAgbXV0YXRlKGNvdW50eV9uYW1lID0gcmVvcmRlcihjb3VudHlfbmFtZSwgcGVyY2VudGFnZSkpICU+JQogIGFycmFuZ2UoZGVzYyhwZXJjZW50YWdlKSkgJT4lCiAgaGVhZCgyMCkKCmdyYXBoX2Rpc3RyaWJ1dGlvbl9ieV9jb3VudHkob3JpZ2luYXRlZF9tb3J0Z2FnZV9kaXN0cmlidXRpb25fYnlfY291bnRpZXMpCgpgYGAKCiMjIEdyYXBoIGhvbWUgbG9hbiBhcHBsaWNhdGlvbiBkaXN0cmlidXRpb24gZm9yIHRoZSB0b3AgNCBjb3VudGllcyBpbiB0aGUgYWJvdmUKIyMgY2hhcnQgYnkgYXBwbGljYW50X3JhY2VfMQpgYGB7cn0KCmNvdW50eV9uYW1lcyA8LSBjKCJBbGxlZ2hlbnkgQ291bnR5IiwgIlBoaWxhZGVscGhpYSBDb3VudHkiLCAiTW9udGdvbWVyeSBDb3VudHkiLCAiQnVja3MgQ291bnR5IikKCmZvciAoY291bnR5X25hbWUgaW4gY291bnR5X25hbWVzKSB7CiAgCiAgaG1kYV9kYXRhX2NvdW50eV9kZiA8LSBobWRhX2RhdGFfcGFfZGZbaG1kYV9kYXRhX3BhX2RmJGNvdW50eV9uYW1lID09IGNvdW50eV9uYW1lLCBdCiAgCiAgbW9ydGdhZ2VfYnlfcmFjZV9jb3VudHkgPC0gaG1kYV9kYXRhX2NvdW50eV9kZiAlPiUgZ3JvdXBfYnkoYXBwbGljYW50X3JhY2VfbmFtZV8xKSAlPiUKICAgIHN1bW1hcmlzZShSYWNlQ291bnQgPSBuKCkpICU+JSBhcnJhbmdlKGRlc2MoUmFjZUNvdW50KSkKICAKICBwcmludChncmFwaF9tb3J0Z2FnZV9kaXN0cmlidXRpb25fYnlfcmFjZTEobW9ydGdhZ2VfYnlfcmFjZV9jb3VudHkpKQp9CgpgYGAKCiMjIEdyYXBoIGluY29tZSBkaXN0cmlidXRpb24gZm9yIFdoaXRlcyBhbmQgQWZyaWNhbiBBbWVyaWNhbnMgcGVyIGNvdW50eSBmb3IgdGhlCiMjIHRvcCA0IGNvdW50aWVzIGFib3ZlLgoKYGBge3J9Cgpmb3IgKGNvdW50eV9uYW1lIGluIGNvdW50eV9uYW1lcykgewogIGhtZGFfb3JpZ2luYXRpb25fc3RhdHVzX2RmX2J5X2NvdW50eV93aGl0ZSA8LSBobWRhX2RhdGFfcGFfZGZbaG1kYV9kYXRhX3BhX2RmJGFjdGlvbl90YWtlbiA9PSAiMSIgJiBobWRhX2RhdGFfcGFfZGYkY291bnR5X25hbWUgPT0gY291bnR5X25hbWUgJiBobWRhX2RhdGFfcGFfZGYkYXBwbGljYW50X3JhY2VfbmFtZV8xID09ICJXaGl0ZSIsIF0KICBwcmludChncmFwaF9hcHBsaWNhbnRfaW5jb21lX2hpc3RvZ3JhbShobWRhX29yaWdpbmF0aW9uX3N0YXR1c19kZl9ieV9jb3VudHlfd2hpdGUsICJJbmNvbWUgZGlzdHJpYnV0aW9uIGZvciBXaGl0ZXMiKSkKCiAgaG1kYV9vcmlnaW5hdGlvbl9zdGF0dXNfZGZfYnlfY291bnR5X2FmcmljYW5fYW1lcmljYW4gPC0gaG1kYV9kYXRhX3BhX2RmW2htZGFfZGF0YV9wYV9kZiRhY3Rpb25fdGFrZW4gPT0gIjEiICYgaG1kYV9kYXRhX3BhX2RmJGNvdW50eV9uYW1lID09IGNvdW50eV9uYW1lICYgaG1kYV9kYXRhX3BhX2RmJGFwcGxpY2FudF9yYWNlX25hbWVfMSA9PSAiQmxhY2sgb3IgQWZyaWNhbiBBbWVyaWNhbiIsIF0KICBwcmludChncmFwaF9hcHBsaWNhbnRfaW5jb21lX2hpc3RvZ3JhbShobWRhX29yaWdpbmF0aW9uX3N0YXR1c19kZl9ieV9jb3VudHlfYWZyaWNhbl9hbWVyaWNhbiwgIkluY29tZSBkaXN0cmlidXRpb24gZm9yIEFmcmljYW4gQW1lcmljYW5zIikpCn0KCmBgYAoKIyMgR3JhcGggaG9tZSBsb2FuIGFwcGxpY2F0aW9uIGRpc3RyaWJ1dGlvbiBmb3IgdGhlIHRvcCA0IGNvdW50aWVzIGluIHRoZSBhYm92ZQojIyBjaGFydCBieSBhcHBsaWNhbnRfcmFjZV9hbmRfZXRobmljaXR5CmBgYHtyfQoKY291bnR5X25hbWVzIDwtIGMoIkFsbGVnaGVueSBDb3VudHkiLCAiUGhpbGFkZWxwaGlhIENvdW50eSIsICJNb250Z29tZXJ5IENvdW50eSIsICJCdWNrcyBDb3VudHkiKQoKZm9yIChjb3VudHlfbmFtZSBpbiBjb3VudHlfbmFtZXMpIHsKICAKICBobWRhX2RhdGFfY291bnR5X2RmIDwtIGhtZGFfZGF0YV9wYV9kZltobWRhX2RhdGFfcGFfZGYkY291bnR5X25hbWUgPT0gY291bnR5X25hbWUsIF0KICAKICBtb3J0Z2FnZV9ieV9yYWNlX2NvdW50eSA8LSBobWRhX2RhdGFfY291bnR5X2RmICU+JSBncm91cF9ieShhcHBsaWNhbnRfcmFjZV9hbmRfZXRobmljaXR5KSAlPiUKICAgIHN1bW1hcmlzZShSYWNlQ291bnQgPSBuKCkpICU+JSBhcnJhbmdlKGRlc2MoUmFjZUNvdW50KSkKICAKICBwcmludChncmFwaF9tb3J0Z2FnZV9kaXN0cmlidXRpb25fYnlfcmFjZV9hbmRfZXRobmljaXR5KG1vcnRnYWdlX2J5X3JhY2VfY291bnR5KSkKfQoKYGBgCgojIEdyYXBoIHdoaWNoIGNvbW11bml0aWVzIGhhdmUgdGhlIGxhcmdlc3QgcHJvcG9ydGlvbiBvZiBsb2FucyBpbiB2YXJpb3VzIHN0YWdlcy4KIyBmb3IgdGhlIHRvcCA0IGNvdW50aWVzIGxpc3RlZCBhYm92ZS4KIyBUaGVzZSBpbmNsdWRlIG9yaWdpbmF0aW9uIHN0YXR1cywgZGVuaWVkIHN0YXR1cywgZXRjLgoKYGBge3J9Cgpmb3IgKGNvdW50eV9uYW1lIGluIGNvdW50eV9uYW1lcykgewogIAogIGhtZGFfZGF0YV9jb3VudHlfZGYgPC0gaG1kYV9kYXRhX3BhX2RmW2htZGFfZGF0YV9wYV9kZiRjb3VudHlfbmFtZSA9PSBjb3VudHlfbmFtZSwgXQogIAogIG1vcnRnYWdlX2J5X3JhY2UxX2NvdW50eSA8LSBobWRhX2RhdGFfY291bnR5X2RmICU+JSBncm91cF9ieShhcHBsaWNhbnRfcmFjZV9hbmRfZXRobmljaXR5KSAlPiUKICAgIHN1bW1hcmlzZShSYWNlQ291bnQgPSBuKCkpICU+JSBhcnJhbmdlKGRlc2MoUmFjZUNvdW50KSkKCiAgbW9ydGdhZ2Vfc3RhdHVzX2J5X3JhY2UxX2J5X2NvdW50eSA8LSBobWRhX2RhdGFfY291bnR5X2RmICU+JSBncm91cF9ieShhY3Rpb25fdGFrZW5fbmFtZSwgYXBwbGljYW50X3JhY2VfYW5kX2V0aG5pY2l0eSkgJT4lCiAgICBzdW1tYXJpc2UoQWN0aW9uQ291bnQgPSBuKCkpICU+JQogICAgYXJyYW5nZShkZXNjKEFjdGlvbkNvdW50KSkKICAKICBtb3J0Z2FnZV9zdGF0dXNfYWdncmVnYXRlZF9ieV9yYWNlMV9ieV9jb3VudHkgID0gaW5uZXJfam9pbihtb3J0Z2FnZV9zdGF0dXNfYnlfcmFjZTFfYnlfY291bnR5LCBtb3J0Z2FnZV9ieV9yYWNlMV9jb3VudHkpICU+JSBtdXRhdGUocGVyY2VudGFnZSA9IChBY3Rpb25Db3VudCAvIFJhY2VDb3VudCkgKiAxMDApCiAgCiAgcHJpbnQoZ3JhcGhfYXBwbGljYXRpb25fcmFjZV9hbmRfZXRobmljaXR5X3Byb3BvcnRpb25fb2ZfbG9hbnMobW9ydGdhZ2Vfc3RhdHVzX2FnZ3JlZ2F0ZWRfYnlfcmFjZTFfYnlfY291bnR5KSkKfQoKYGBgCgojIFZpc3VhbGl6ZSBtaXNzaW5nIHZhcmlhYmxlcwpOb3cgd2Ugc3RhcnQgbG9va2luZyBhdCB0aGUgbWlzc2luZyB2YWx1ZXMgYW5kIHNlZSBob3cgY2FuIHdlIGRlYWwgd2l0aCB0aGVtIC5TbyBoZXJlLCB3ZSB0cnkgYW5kIHZpenVhbGl6ZSB0aGUgbWlzc2luZyB2YWx1ZXMKYGBge3J9Cgp2aXN1YWxpemVfbWlzc2luZ192YWx1ZXMoaG1kYV9kYXRhX3BhX2RmKQoKYGBgCkluIHRoaXMgZ3JhcGgsIHdlIHNlZSB0aGUgbWlzc2luZyB2YWx1ZSBjb3VudCBmb3IgZWFjaCBjb2x1bW4gYW5kIGZvciBlYWNoIGNhdGVnb3J5IHRvby4gVGhlcmUgYXJlIGFsb3Qgb2YgbWlzc2luZyBpbiBzb21lIGNvbHVtbnMgbGlrZSBjbyBhcHBsaWNhbnQgYW5kIGFwcGxpY2FudCAyLTMtNCByYWNlLgoKTm93IHdlIHRyeSB0byBpbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzLiBFYXN5IHdheSBvdXQgaGVyZSBpcyB0byBpbXB1dGUgaXQgd2l0aCBtaWNlIGZ1bmN0aW9uLiBJdHMgbm90IHRoZSBiZXN0IGJ1dCBpbml0aWFsbHkgd2UgZ28gd2l0aCB0aGlzIGFuZCBzZWUgaG93IGl0IHBlcmZvcm1zLiAjIEltcHV0ZSBhcyBuZWVkZWQuCgojIEltcHV0ZSBhcyBuZWVkZWQuCmBgYHtyfQoKIyBodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvbWljZS92ZXJzaW9ucy8zLjguMC90b3BpY3MvbWljZS5pbXB1dGUuY2FydApobWRhX2RhdGFfcGFfZGZfaW1wdXRlZCA8LSBtaWNlKGhtZGFfZGF0YV9wYV9kZiwgbT0xLCBtYXhpdD0yLCBtZXRoPSdjYXJ0JyxzZWVkPTUwMCkKCmhtZGFfZGF0YV9wYV9kZl9pbXB1dGVkIDwtIG1pY2U6OmNvbXBsZXRlKGhtZGFfZGF0YV9wYV9kZl9pbXB1dGVkKQoKYGBgCgojIyBNb3JlIGFuYWx5c2lzIG9uIHRoZSBpbXB1dGVkIGRhdGFzZXQuCmBgYHtyfQoKc3VtbWFyeShobWRhX2RhdGFfcGFfZGZfaW1wdXRlZCkKCmdnX21pc3NfdXBzZXQoaG1kYV9kYXRhX3BhX2RmX2ltcHV0ZWQpCgpgYGAKIyBBZGRpdGlvbmFsIGFuYWx5c2lzIG9uIHRoZSBobWRhIGRhdGFzZXQuIGNvcnJlbGF0aW9uIG1hdHJpeCwgcGxvdHMsIGV0Yy4KYGBge3J9CiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjA2MzczNjAvY29udmVydC1hbGwtZGF0YS1mcmFtZS1jaGFyYWN0ZXItY29sdW1ucy10by1mYWN0b3JzCmhtZGFfZGF0YV9wYV9kZiRsb2FuX3RvX2luY29tZV9yYXRpbyA8LSBobWRhX2RhdGFfcGFfZGYkbG9hbl9hbW91bnRfMDAwcyAvIGhtZGFfZGF0YV9wYV9kZiRhcHBsaWNhbnRfaW5jb21lXzAwMHMKCmhtZGFfZGF0YV9wYV9kZltzYXBwbHkoaG1kYV9kYXRhX3BhX2RmLCBpcy5jaGFyYWN0ZXIpXSA8LSBsYXBwbHkoaG1kYV9kYXRhX3BhX2RmW3NhcHBseShobWRhX2RhdGFfcGFfZGYsIGlzLmNoYXJhY3RlcildLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuZmFjdG9yKQoKaG1kYV9kYXRhX3BhX2RmX2Zvcl9jb3JyZWxhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKGxhcHBseShobWRhX2RhdGFfcGFfZGYsIGFzLmludGVnZXIpKQoKI2hlYWQoaG1kYV9kYXRhX3BhX2RmX2Zvcl9jb3JyZWxhdGlvblssIGMoImFwcGxpY2FudF9pbmNvbWVfMDAwcyIsICJsb2FuX2Ftb3VudF8wMDBzIildKQoKaGVhZChobWRhX2RhdGFfcGFfZGZfZm9yX2NvcnJlbGF0aW9uKQoKY29ycl9zaW1wbGUoaG1kYV9kYXRhX3BhX2RmX2Zvcl9jb3JyZWxhdGlvbikKCmNvcnJwbG90KGNvcihobWRhX2RhdGFfcGFfZGZfZm9yX2NvcnJlbGF0aW9uWywgYygiYXBwbGljYW50X2luY29tZV8wMDBzIiwgImxvYW5fYW1vdW50XzAwMHMiKV0sIHVzZSA9ICJuYS5vci5jb21wbGV0ZSIpKQoKYGBgCgoKIyBBZGRpdGlvbmFsIGFuYWx5c2lzIG9uIHRoZSBobWRhIGltcHV0ZWQgZGF0YXNldC4gY29ycmVsYXRpb24gcGxvdHMsIGV0Yy4KYGBge3J9CiMgaG1kYV9kYXRhX3BhX2RmX2ltcHV0ZWQgPC0gaG1kYV9kYXRhX3BhX2RmOwojIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzIwNjM3MzYwL2NvbnZlcnQtYWxsLWRhdGEtZnJhbWUtY2hhcmFjdGVyLWNvbHVtbnMtdG8tZmFjdG9ycwpobWRhX2RhdGFfcGFfZGZfaW1wdXRlZCRsb2FuX3RvX2luY29tZV9yYXRpbyA8LSBobWRhX2RhdGFfcGFfZGZfaW1wdXRlZCRsb2FuX2Ftb3VudF8wMDBzIC8gaG1kYV9kYXRhX3BhX2RmX2ltcHV0ZWQkYXBwbGljYW50X2luY29tZV8wMDBzCgpobWRhX2RhdGFfcGFfZGZfaW1wdXRlZFtzYXBwbHkoaG1kYV9kYXRhX3BhX2RmX2ltcHV0ZWQsIGlzLmNoYXJhY3RlcildIDwtIGxhcHBseShobWRhX2RhdGFfcGFfZGZfaW1wdXRlZFtzYXBwbHkoaG1kYV9kYXRhX3BhX2RmX2ltcHV0ZWQsIGlzLmNoYXJhY3RlcildLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuZmFjdG9yKQoKaG1kYV9kYXRhX3BhX2RmX2ltcHV0ZWRfZm9yX2NvcnJlbGF0aW9uIDwtIGFzLmRhdGEuZnJhbWUobGFwcGx5KGhtZGFfZGF0YV9wYV9kZl9pbXB1dGVkLCBhcy5pbnRlZ2VyKSkKCmhlYWQoaG1kYV9kYXRhX3BhX2RmX2ltcHV0ZWRfZm9yX2NvcnJlbGF0aW9uWywgYygiYXBwbGljYW50X2luY29tZV8wMDBzIiwgImxvYW5fYW1vdW50XzAwMHMiKV0pCgpjb3JyX3NpbXBsZShobWRhX2RhdGFfcGFfZGZfaW1wdXRlZF9mb3JfY29ycmVsYXRpb24pCgpjb3JycGxvdChjb3IoaG1kYV9kYXRhX3BhX2RmX2ltcHV0ZWRfZm9yX2NvcnJlbGF0aW9uWywgYygiYXBwbGljYW50X2luY29tZV8wMDBzIiwgImxvYW5fYW1vdW50XzAwMHMiKV0sIHVzZSA9ICJuYS5vci5jb21wbGV0ZSIpKQoKYGBgCgojIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIHJhY2UgYW5kIGFwcHJvdmVkIGxvYW5zLgpgYGB7cn0KCmhtZGFfbW9kZWxfZGYgPC0gaG1kYV9kYXRhX2ZyYW1lX2Zvcl9tb2RlbChobWRhX2RhdGFfcGFfZGZfaW1wdXRlZCkKaG1kYV9tb2RlbF9kZiA8LSBwcm9jZXNzX21vZGVsX2RmX2NvbHVtbnMoaG1kYV9tb2RlbF9kZikKCmwgPC0gZ2dwbG90KGhtZGFfbW9kZWxfZGYsIGFlcyhhcHBsaWNhbnRfcmFjZV9hbmRfZXRobmljaXR5LGZpbGwgPSBsb2FuX2dyYW50ZWQpKQpsIDwtIGwgKyBnZW9tX2hpc3RvZ3JhbShzdGF0PSJjb3VudCIpICsgY29vcmRfZmxpcCgpCnByaW50KGwpCgoKYGBgCgojIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIGxvYW4gcHVycG9zZSBhbmQgbG9hbiBhY3Rpb24uCmBgYHtyfQpsIDwtIGdncGxvdChobWRhX21vZGVsX2RmLCBhZXMobG9hbl9wdXJwb3NlLCBmaWxsID0gbG9hbl9ncmFudGVkKSkKbCA8LSBsICsgZ2VvbV9oaXN0b2dyYW0oc3RhdD0iY291bnQiKSArIGNvb3JkX2ZsaXAoKQpwcmludChsKQoKYGBgCgpgYGB7cn0KcGxvdChobWRhX21vZGVsX2RmJGxvYW5fZ3JhbnRlZCwgbWFpbj0iTG9hbiBncmFudGVkIFZhcmlhYmxlIiwKICAgICBjb2w9Y29sb3JzKClbMTAwOjEwMl0sCiAgICAgeGxhYj0iTG9hbiBkaXN0cmlidXRpb24iKQoKYGBgCgojIyMgQXBwbGljYW50cyAgbG9hbiBhbW91bnQKYGBge3J9CnNrZXcgPC0gcGFzdGUoIlNrZXduZXNzOiIsc2tld25lc3MoaG1kYV9tb2RlbF9kZiRsb2FuX2Ftb3VudF8wMDBzLG5hLnJtID0gVFJVRSkpCmdncGxvdChkYXRhID0gaG1kYV9tb2RlbF9kZiAsIGFlcyh4ID0gbG9hbl9hbW91bnRfMDAwcykpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJzdGVlbGJsdWUiKSArIGxhYnModGl0bGUgPSAiTG9hbiBhbW91bnQgZGlzdHJpYnV0aW9uIiAsIHggPSAiTG9hbiBhbW91bnQgaW4gdGhvdXNhbmRzIiAsIHkgPSAiQ291bnQiKSsgYW5ub3RhdGUoInRleHQiLCB4ID0gMTAwMDAwLCB5ID0gMzAwMDAwLCBzaXplID0gMy4yLGxhYmVsID0gc2tldykKYGBgCgpMb29rcyBsaWtlIHRoZSBkYXRhIGlzIGhpZ2hseSBza2V3ZWQuCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygibW9tZW50cyIpCmxpYnJhcnkobW9tZW50cykKc2tld25lc3MoaG1kYV9tb2RlbF9kZiRsb2FuX2Ftb3VudF8wMDBzLG5hLnJtID0gVFJVRSkKYGBgCgoKVGhlIGRhdGEgZm9yIGxvYW4gYW1vdW50IGlzIGhpZ2hseSByaWdodCBza2V3ZWQuIENoYW5nZXMgc2hvdWxkIGJlIG1hZGUgc28gdGhhdCB0aGUgcHJlZGljdGlvbiBtb2RlbCBkb2VzIG5vdCBtZXNzIHVwLgoKIyMgSGFuZGxpbmcgaGlnaGx5IHNrZXdlZCBkYXRhLiBMb2cgVHJhbnNmb3JtYXRpb24KYGBge3J9CnNrZXcgPC0gcGFzdGUoIlNrZXduZXNzOiIsc2tld25lc3MobG9nKGhtZGFfbW9kZWxfZGYkbG9hbl9hbW91bnRfMDAwcyksbmEucm0gPSBUUlVFKSkKZ2dwbG90KGRhdGEgPSBobWRhX21vZGVsX2RmICwgYWVzKHggPSBsb2cobG9hbl9hbW91bnRfMDAwcykpKSArIGdlb21faGlzdG9ncmFtKGZpbGwgPSAic3RlZWxibHVlIikgKyBsYWJzKHRpdGxlID0gIkxvZyB0cmFuc2Zvcm1lZCBkaXN0cmlidXRpb24gZm9yIExvYW4gYW1vdW50IiAsIHggPSAibG9nKExvYW4gQW1vdW50KSIsIHkgPSAnQ291bnQnKSsgYW5ub3RhdGUoInRleHQiLCB4ID0gOCwgeSA9IDEwMDAwMCwgc2l6ZSA9IDMuMixsYWJlbCA9IHNrZXcpCmBgYAoKYGBge3J9CnNrZXduZXNzKGxvZyhobWRhX21vZGVsX2RmJGxvYW5fYW1vdW50XzAwMHMpLG5hLnJtID0gVFJVRSkKYGBgCgojIEJveHBsb3Qgb2YgbG9nIG9mIGxvYW4gYW1vdW50cy4KYGBge3J9CmJveHBsb3QobG9nKGhtZGFfbW9kZWxfZGYkbG9hbl9hbW91bnRfMDAwcyksY29sID0gY29sb3JzKClbMTAwOjEwOV0sCiAgICAgICAgbWFpbiA9ICJCb3hwbG90IG9mIExvZyBvZiBMb2FuIEFtb3VudHMiLAogICAgICAgIHhsYWI9IkxvYW4gQW1vdW50IiwKICAgICAgICB5bGFiPSJEaXN0cmlidXRpb24gb2YgTG9nIG9mIExvYW4gQW1vdW50cyIpCmBgYAoKIyMjIFNhbWUgaXMgdGhlIGNhc2Ugd2l0aCBhcHBsaWNhbnRzIGluY29tZQpgYGB7cn0Kc2tldyA8LSBwYXN0ZSgiU2tld25lc3M6Iixza2V3bmVzcyhobWRhX21vZGVsX2RmJGFwcGxpY2FudF9pbmNvbWVfMDAwcyxuYS5ybSA9IFRSVUUpKQpnZ3Bsb3QoZGF0YSA9IGhtZGFfbW9kZWxfZGYgLCBhZXMoeCA9IGFwcGxpY2FudF9pbmNvbWVfMDAwcykpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJzdGVlbGJsdWUiKSArIGxhYnModGl0bGUgPSAiQXBwbGljYW50IEluY29tZSBkaXN0cmlidXRpb24iICwgeCA9ICJBcHBsaWNhbnQgSW5jb21lIGluIHRob3VzYW5kcyIgLCB5ID0gIkNvdW50IikgKyBhbm5vdGF0ZSgidGV4dCIsIHggPSAxMDAwMDAsIHkgPSA5MDAwMCwgc2l6ZSA9IDMuMixsYWJlbCA9IHNrZXcpCmBgYAoKYGBge3J9CnNrZXcgPC0gcGFzdGUoIlNrZXduZXNzOiIsc2tld25lc3MobG9nKGhtZGFfbW9kZWxfZGYkYXBwbGljYW50X2luY29tZV8wMDBzKSxuYS5ybT1UUlVFKSkKZ2dwbG90KGRhdGEgPSBobWRhX21vZGVsX2RmICwgYWVzKHggPSBsb2coYXBwbGljYW50X2luY29tZV8wMDBzKSkpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJzdGVlbGJsdWUiKSArIGxhYnModGl0bGUgPSAiTG9nIHRyYW5zZm9ybWVkIGRpc3RyaWJ1dGlvbiBmb3IgQXBwbGljYW50IEluY29tZSIgLCB4ID0gImxvZyhBcHBsaWNhbnQgSW5jb21lKSIsIHkgPSAnQ291bnQnKSArYW5ub3RhdGUoInRleHQiLCB4ID0gMTAsIHkgPSA5MDAwMCwgc2l6ZSA9IDMuMixsYWJlbCA9IHNrZXcpCmBgYAoKCiMgQm94IHBsb3RzIGZvciBsb2cgb2YgbG9hbiBhbW91bnRzIHZzIGRlY2lzaW9uCmBgYHtyfQoKYm94cGxvdChsb2cobG9hbl9hbW91bnRfMDAwcyl+bG9hbl9ncmFudGVkLCB4bGFiPSJMb2FuIGRlY2lzaW9uIix5bGFiPSJMb2cgb2YgTG9hbiBBbW91bnRzIixjb2w9YygicGluayIsImxpZ2h0Ymx1ZSIpLAogICAgICAgIG1haW49IkV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgUGxvdFxuIG9mIExvYW4gRGVjaXNpb24gVmVyc3VzIExvZyBvZiBMb2FuIEFtb3VudHMiLCBkYXRhID0gaG1kYV9tb2RlbF9kZikKCmBgYAoKIyBCb3ggcGxvdHMgZm9yIGxvZyBvZiBsb2FuIGFtb3VudHMgdnMgZGVjaXNpb24KYGBge3J9Cgpib3hwbG90KGxvZyhhcHBsaWNhbnRfaW5jb21lXzAwMHMpfmxvYW5fZ3JhbnRlZCwgeGxhYj0iTG9hbiBkZWNpc2lvbiIseWxhYj0iTG9nIG9mIEFwcGxpY2FudCBJbmNvbWUiLGNvbD1jKCJwaW5rIiwibGlnaHRibHVlIiksCiAgICAgICAgbWFpbj0iRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyBQbG90XG4gb2YgTG9hbiBEZWNpc2lvbiBWZXJzdXMgTG9nIG9mIEFwcGxpY2FudCBJbmNvbWUiLCBkYXRhID0gaG1kYV9tb2RlbF9kZikKCmBgYAogCiMgUGxvdCBmb3IgbG9nIG9mIGFwcGxpY2FudCBpbmNvbWUsIHJhY2UgYW5kIGV0aG5pY2l0eSB3aXRoIGNvbG9yIGJ5IGxvYW4gZGVjaXNpb24uIApgYGB7cn0KZ2dwbG90KGhtZGFfbW9kZWxfZGYsIGFlcyhsb2coYXBwbGljYW50X2luY29tZV8wMDBzKSwgYXBwbGljYW50X3JhY2VfYW5kX2V0aG5pY2l0eSwgY29sb3IgPSBsb2FuX2dyYW50ZWQpKSArIAogIGdlb21faml0dGVyKCkgKwogIGdndGl0bGUoIkxvZyBvZiBBcHBsaWNhbnQgaW5jb21lIHZzLiBBcHBsaWNhbnQgcmFjZSBhbmQgZXRobmljaXR5ICwgYnkgIGNvbG9yID0gTG9hbiBkZWNpc2lvbiIpICsKICB0aGVtZV9saWdodCgpCgpgYGAKCiMgUGxvdCBmb3IgbG9nIG9mIGxvYW4gYW1vdW50cywgcmFjZSBhbmQgZXRobmljaXR5IHdpdGggY29sb3IgYnkgbG9hbiBkZWNpc2lvbi4gCmBgYHtyfQpnZ3Bsb3QoaG1kYV9tb2RlbF9kZiwgYWVzKGxvZyhsb2FuX2Ftb3VudF8wMDBzKSwgYXBwbGljYW50X3JhY2VfYW5kX2V0aG5pY2l0eSwgY29sb3IgPSBsb2FuX2dyYW50ZWQpKSArIAogIGdlb21faml0dGVyKCkgKwogIGdndGl0bGUoIkxvZyBvZiBsb2FuIGFtb3VudCB2cy4gQXBwbGljYW50IHJhY2UgYW5kIGV0aG5pY2l0eSAsIGJ5ICBjb2xvciA9IExvYW4gZGVjaXNpb24iKSArCiAgdGhlbWVfbGlnaHQoKQoKYGBgCgojIFBsb3QgZm9yIGxvYW4gdG8gaW5jb21lIHJhdGlvLCByYWNlIGFuZCBldGhuaWNpdHkgd2l0aCBjb2xvciBieSBsb2FuIGRlY2lzaW9uLiAKYGBge3J9CgpnZ3Bsb3QoaG1kYV9tb2RlbF9kZiwgYWVzKGxvYW5fdG9faW5jb21lX3JhdGlvLCBhcHBsaWNhbnRfcmFjZV9hbmRfZXRobmljaXR5LCBjb2xvciA9IGxvYW5fZ3JhbnRlZCkpICsgCiAgZ2VvbV9qaXR0ZXIoKSArCiAgZ2d0aXRsZSgiTG9hbiB0byBJbmNvbWUgcmF0aW8gdnMuIEFwcGxpY2FudCByYWNlIGFuZCBldGhuaWNpdHkgLCBieSAgY29sb3IgPSBMb2FuIGRlY2lzaW9uIikgKwogIHRoZW1lX2xpZ2h0KCkKCmBgYAoKCiMgU2F2ZSB0aGUgaW1wdXRlZCBkYXRhZnJhbWUgdG8gZmlsZS4KYGBge3J9Cgp3cml0ZS5jc3YoaG1kYV9kYXRhX3BhX2RmX2ltcHV0ZWQsIHBhc3RlKGRhdGFfZGlyLCAiLzIwMTYvaG1kYV8yMDE2X3BhX2ltcHV0ZWQuY3N2Iiwgc2VwID0gIiIpLCByb3cubmFtZXMgPSBGQUxTRSkKYGBg